from typing import Annotated, Optional
import re

from fastapi import Query, Body
from pydantic import BaseModel, model_validator

from app.utils import regexes, types
from app.models.common import AccountId, AppId, Pagination
from app.models import responses
from app.database.models import ALLOW_CREATE_IDENTITY, ALLOW_UPDATE_IDENTITY


class AccountsList(Pagination):
    """分页获取账号列表"""
    username: str
    identity: Optional[int]

    @model_validator(mode='before')
    @classmethod
    def before(cls, data):
        data = types.safe_dict(data)
        data['username'] = data.get('username', '')
        data['identity'] = types.safe_int(data.get('identity', None), None)
        return data

    @model_validator(mode='after')
    def after(self):
        if self.identity is not None and not (0 <= self.identity <= 255):
            raise responses.FailureResponseError(-102, msg='账号身份未知, 无法筛选')

        if len(self.username) > 32:
            raise responses.FailureResponseError(-102, msg='用户名长度不能超过 32 位')

        return self


AccountsListParams = Annotated[AccountsList, Query()]


class CreateAccount(BaseModel):
    """创建账号"""
    username: str
    email: str
    phone: str
    password: str
    identity: int
    max_apps: int

    @model_validator(mode='before')
    @classmethod
    def before(cls, data):
        data = types.safe_dict(data)
        data['username'] = data.get('username', '')
        data['email'] = data.get('email', '')
        data['phone'] = data.get('phone', '')
        data['password'] = data.get('password', '')
        data['identity'] = types.safe_int(data.get('identity', None), 0)
        data['max_apps'] = types.safe_int(data.get('max_apps', None), 100)
        return data

    @model_validator(mode='after')
    def after(self):
        if not (6 <= len(self.username) <= 32):
            raise responses.FailureResponseError(-102, msg='用户名长度应介于 6-32 位')

        if not re.search(re.compile(regexes.ENGLISH_NUMBER), self.username):
            raise responses.FailureResponseError(-102, msg='用户名只能为字母和数字组合')

        if len(self.email) > 320:
            raise responses.FailureResponseError(-102, msg='绑定邮箱的长度不能超过 320 位')

        if not re.fullmatch(regexes.EMAIL, self.email):
            raise responses.FailureResponseError(code=-102, msg='邮箱格式不正确')

        if not (8 <= len(self.phone) <= 24):
            raise responses.FailureResponseError(-102, msg='必须填写完整的手机号码')

        if not re.search(re.compile(regexes.PASSWORD), self.password):
            raise responses.FailureResponseError(-102, msg='密码至少 8 位且必须含大小写字母以及特殊字符(@+#_)中的一个')

        if self.identity not in ALLOW_CREATE_IDENTITY:
            raise responses.FailureResponseError(-102, msg='额外的账号身份无法创建')

        if not (0 <= self.max_apps <= 255):
            raise responses.FailureResponseError(-102, msg='账号创建的应用的上下限为：0-255')

        return self


CreateAccountJSON = Annotated[CreateAccount, Body()]


class ModifyPassword(AccountId):
    """修改密码"""
    new_password: str

    @model_validator(mode='before')
    @classmethod
    def before(cls, data):
        data = types.safe_dict(data)
        data['new_password'] = data.get('new_password', '')
        return data

    @model_validator(mode='after')
    def after(self):
        if not re.search(re.compile(regexes.PASSWORD), self.new_password):
            raise responses.FailureResponseError(
                -102, msg='新密码至少 8 位且必须含大小写字母以及特殊字符(@+#%&_+=)中的一个')

        return self


ModifyPasswordJSON = Annotated[ModifyPassword, Body()]


class DisableAccount(AccountId):
    """是否禁用账号"""
    is_banned: bool = True


DisableAccountJSON = Annotated[DisableAccount, Body()]


class UpdateAccount(AccountId):
    """更新账号"""
    username: Optional[str]
    email: Optional[str]
    phone: Optional[str]
    identity: Optional[int]
    max_apps: Optional[int]
    remark: Optional[str]

    @model_validator(mode='before')
    @classmethod
    def before(cls, data):
        data = types.safe_dict(data)
        data['username'] = data.get('username', None)
        data['email'] = data.get('email', None)
        data['phone'] = data.get('phone', None)
        data['identity'] = types.safe_int(data.get('identity', None), None)
        data['max_apps'] = types.safe_int(data.get('max_apps', None), None)
        data['remark'] = data.get('remark', None)
        return data

    @model_validator(mode='after')
    def after(self):
        if self.username is not None:
            if not (6 <= len(self.username) <= 32):
                raise responses.FailureResponseError(-102, msg='用户名长度应介于 6-32 位')

            if not re.search(re.compile(regexes.ENGLISH_NUMBER), self.username):
                raise responses.FailureResponseError(-102, msg='用户名只能为字母和数字组合')

        if self.email is not None:
            if len(self.email) > 320:
                raise responses.FailureResponseError(-102, msg='绑定邮箱的长度不能超过 320 位')
            if not re.fullmatch(regexes.EMAIL, self.email):
                raise responses.FailureResponseError(code=-102, msg='邮箱格式不正确')

        if self.phone is not None and not (8 <= len(self.phone) <= 24):
            raise responses.FailureResponseError(-102, msg='必须填写完整的手机号码')

        if self.identity is not None and self.identity not in ALLOW_UPDATE_IDENTITY:
            raise responses.FailureResponseError(-102, msg='额外的账号身份无法更新')

        if self.max_apps is not None and not (0 <= self.max_apps <= 255):
            raise responses.FailureResponseError(-102, msg='账号创建的应用的上下限为：0-255')

        if self.remark is not None and len(self.remark) > 512:
            raise responses.FailureResponseError(-102, msg='账号的备注长度不能超过 512 个字符')

        return self


UpdateAccountJSON = Annotated[UpdateAccount, Body()]


class AccountAppList(AccountId, Pagination):
    """分页获取账号的应用列表"""
    name: str

    @model_validator(mode='before')
    @classmethod
    def before(cls, data):
        data = types.safe_dict(data)
        data['name'] = data.get('name', '')
        return data

    @model_validator(mode='after')
    def after(self):
        if len(self.name) > 64:
            raise responses.FailureResponseError(-102, msg='查询的应用名称长度不能超过 64 个字符')

        return self


AccountAppListParams = Annotated[AccountAppList, Query()]


class UpdateAccountApp(AppId):
    """更新某账号的应用"""
    name: Optional[str]
    notice: Optional[str]
    force_disabled: Optional[bool]
    is_disabled: Optional[bool]
    allow_recharge: Optional[bool]
    max_bind: Optional[int]
    time_restriction: Optional[str]
    unbind_enabled: Optional[bool]
    unbind_cost: Optional[int]
    max_trial: Optional[int]
    trial_duration: Optional[int]
    ping_duration: Optional[int]
    card_length: Optional[int]
    card_prefix: Optional[str]
    card_rule: Optional[str]
    extra_data: Optional[dict]

    @model_validator(mode='before')
    @classmethod
    def before(cls, data):
        data = types.safe_dict(data)
        data['name'] = data.get('name', None)
        data['notice'] = data.get('notice', None)
        data['force_disabled'] = data.get('force_disabled', None)
        data['is_disabled'] = data.get('is_disabled', None)
        data['allow_recharge'] = data.get('allow_recharge', None)
        data['max_bind'] = types.safe_int(data.get('max_bind', None), None)
        data['time_restriction'] = data.get('time_restriction', None)
        data['unbind_enabled'] = data.get('unbind_enabled', None)
        data['unbind_cost'] = types.safe_int(data.get('unbind_cost', None), None)
        data['max_trial'] = types.safe_int(data.get('max_trial', None), None)
        data['trial_duration'] = types.safe_int(data.get('trial_duration', None), None)
        data['ping_duration'] = types.safe_int(data.get('ping_duration', None), None)
        data['card_length'] = types.safe_int(data.get('card_length', None), None)
        data['card_prefix'] = data.get('card_prefix', None)
        data['card_rule'] = data.get('card_rule', None)
        data['extra_data'] = types.safe_loads_json(data.get('extra_data')) or None

        return data

    @model_validator(mode='after')
    def after(self):
        if self.name is not None and not (2 <= len(self.name) <= 64):
            raise responses.FailureResponseError(-102, msg='应用名称长度应介于 2-64 位')

        if self.notice is not None and len(self.notice) > 512:
            raise responses.FailureResponseError(-102, msg='应用公告的长度不能超过 512 个字符')

        if self.max_bind is not None and not (1 <= self.max_bind <= 255):
            raise responses.FailureResponseError(-102, msg='应用卡密绑定设备的上下限为：1-255')

        if self.time_restriction is not None and not re.fullmatch(regexes.TIMERANGE, self.time_restriction):
            raise responses.FailureResponseError(-102, msg='登录限制时段格式参考 00:00:00-23:59:59')

        if self.unbind_cost is not None and not (0 <= self.unbind_cost <= 65535):
            raise responses.FailureResponseError(-102, msg='解绑扣时时长应介于 0-65535 之间')

        if self.max_trial is not None and not (0 <= self.max_trial <= 255):
            raise responses.FailureResponseError(-102, msg='最大试用次数的上下限为：0-255')

        if self.trial_duration is not None and not (10 <= self.trial_duration <= 65535):
            raise responses.FailureResponseError(-102, msg='试用时长应介于 10-65535 分钟')

        if self.ping_duration is not None and not (10 <= self.ping_duration <= 65535):
            raise responses.FailureResponseError(-102, msg='心跳失效时间应介于 10-65535 分钟')

        if self.card_length is not None and not (10 <= self.card_length <= 48):
            raise responses.FailureResponseError(-102, msg='卡密长度应介于 10-48 之间')

        if self.card_prefix is not None:
            if len(self.card_prefix) > 16:
                raise responses.FailureResponseError(-102, msg='卡密前缀长度不能超过 16 个字符')
            if re.fullmatch(regexes.INCLUDE_CHINESE, self.card_prefix):
                raise responses.FailureResponseError(-102, msg='卡密前缀不能包含中文字符')

        if self.card_rule is not None:
            card_rule = ''.join(list(dict.fromkeys(self.card_rule)))
            if not (32 <= len(card_rule) <= 128):
                raise responses.FailureResponseError(-102, msg='卡密规则字符个数应在 32-128 之间')

            if re.fullmatch(regexes.INCLUDE_CHINESE, card_rule):
                raise responses.FailureResponseError(-102, msg='卡密规则不能包含中文字符')

        if self.extra_data is not None:
            if len(str(self.extra_data)) > 1024:
                raise responses.FailureResponseError(-102, msg='额外数据长度不能超过 1024 个字符')

        return self


UpdateAccountAppJSON = Annotated[UpdateAccountApp, Body()]


class BatchDeleteAccounts(BaseModel):
    account_ids: list[str] = []

    @model_validator(mode='before')
    @classmethod
    def before(cls, data):
        data = types.safe_dict(data)
        account_ids = []
        for raw_account_id in list(data.get('account_ids', [])):
            account_id = str(raw_account_id)
            if account_id is not None:
                account_ids.append(account_id)

        data['account_ids'] = account_ids
        return data

    @model_validator(mode='after')
    def after(self):
        if type(self.account_ids) is not list:
            raise responses.FailureResponseError(-102, msg='多个账号 ID 必须是列表类型')

        if len(self.account_ids) == 0:
            raise responses.FailureResponseError(-102, msg='请传入至少 1 个账号 ID')

        return self


BatchDeleteAccountsJSON = Annotated[BatchDeleteAccounts, Body()]
